Skip to content

Conversation

erikhfj
Copy link
Member

@erikhfj erikhfj commented Sep 25, 2026 •

What this does

Sets up the repo tooling for #1 so the four of us can work in parallel and the same quality gates run locally and in CI.

Piece Choice
Workspace pnpm with client/ only. server/ and e2e/ join in M2 and M4 rather than existing as empty packages now.
TypeScript tsconfig.base.json with strict and noUncheckedIndexedAccess
Lint ESLint 9 flat config - typescript-eslint, react-hooks, jsx-a11y, react-refresh
Format Prettier, applied last via eslint-config-prettier so the two never fight
Scripts dev, build, lint, lint:fix, format, format:check, typecheck, test, test:watch
Docs AGENTS.md with the architecture rules we agreed on

The client is a skeleton only - it renders the product name, a skip link and a main landmark. Its whole job is to prove the gates are green before #9 replaces it with the mock-data search interface.

What was done

  • pnpm workspace (pnpm-workspace.yaml) declaring client/
  • tsconfig.base.json extended by the client
  • eslint.config.js - TypeScript rules for everything, React + a11y rules scoped to client/**
  • .prettierrc.json / .prettierignore
  • .gitignore including *.tsbuildinfo
  • Root package.json with the scripts above
  • AGENTS.md - the rules that make the graded requirements work (SQL-side sort/filter, keyset pagination, DataLoaders, simulated users, plain CSS, accessibility non-negotiable, search state in the URL)
  • client/ minimal React + Vite + Vitest setup

What was tested

Ran from a clean pnpm install:

pnpm format:check   ✅  all files formatted
pnpm lint           ✅  0 errors, 0 warnings
pnpm typecheck      ✅  tsc -b --noEmit clean
pnpm test           ✅  3 tests passed
pnpm build          ✅  220 kB JS (68.8 kB gzip), 1.1 kB CSS

The three tests cover:

  1. the heading and tagline render
  2. the skip link is present and points at #main
  3. a main landmark exists

Two accessibility details are already wired in so #9 inherits them rather than retrofitting: the skip link, and :focus-visible outlines. prefers-color-scheme and prefers-reduced-motion are in from the start.

Notes on choices

  • noUncheckedIndexedAccess is annoying to write against. It is deliberate - it catches the "index might be undefined" class of bug at compile time, which is exactly the kind that surfaces later as a blank row in the results list.
  • 0 warnings, not "nearly clean". pnpm lint is what CI runs, so the bar has to be enforceable rather than aspirational.
  • Plain CSS, no framework. Smaller bundle for bærekraft, and full control over focus rings and contrast for tilgjengelighet. Same call as Project 1.
  • Only client/ in the workspace. Empty server//e2e/ packages would show the eventual shape but add nothing to review today.

Closes

Closes #1.

Sets up the tooling so four people can work in parallel and the same
quality gates run locally and in CI.

- pnpm workspace with client/ only. server/ and e2e/ join in M2 and M4
  rather than existing as empty packages now.
- tsconfig.base.json: strict + noUncheckedIndexedAccess.
- ESLint 9 flat config (typescript-eslint, react-hooks, jsx-a11y,
  react-refresh) with eslint-config-prettier applied last. The bar is
  0 warnings so CI can enforce it.
- Prettier with .prettierrc.json and .prettierignore.
- Root scripts: dev, build, lint, format, format:check, typecheck, test.
- AGENTS.md documenting the architecture rules we agreed on (SQL-side
  sort/filter, keyset pagination, DataLoaders, simulated users, plain
  CSS, accessibility non-negotiable, search state in the URL).
- A minimal client that renders the product name, a skip link and a main
  landmark, purely to prove the gates are green. #9 replaces it with the
  mock-data search interface.

Co-Authored-By: opencode <noreply@opencode.ai>
@erikhfj erikhfj requested a review from a team as a code owner September 25, 2026 18:33
House style is plain hyphens. No functional change.

Co-Authored-By: opencode <noreply@opencode.ai>
Copy link
Member

@rachelks rachelks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pnpm format:check fails for me on a clean checkout on Windows and reports formatting issues in 17 files. I checked the line endings and get i/lf w/crlf, and I have core.autocrlf=true, so it looks like an LF/CRLF issue rather than the files actually being formatted incorrectly. Could we add .gitattributes or something similar so the format check works consistently on Windows too?

Found in review of #32: pnpm format:check fails on a clean checkout on
Windows for 17 files that are actually formatted correctly. The cause is
core.autocrlf=true, the Git for Windows default, which rewrites the
checked-out files to CRLF. Prettier then sees different bytes than the
ones committed and reports failures that do not exist.

Setting `* text=auto eol=lf` means all four of us see the same bytes
regardless of platform, so format:check behaves identically everywhere.
Shell scripts keep LF for shebangs; batch files get CRLF deliberately;
binary assets are excluded from normalization.

Renormalizing the tree produced no diff, so the committed files were
already LF. This only fixes the checkout side.

Reported by rachelks.

Co-Authored-By: opencode <noreply@opencode.ai>
@erikhfj
Copy link
Member Author

erikhfj commented Sep 28, 2026

Good catch, and this was a real bug rather than a config nit. Thank you for testing with core.autocrlf=true explicitly, that made the cause obvious.

Cause: Git for Windows defaults to core.autocrlf=true, which rewrites checked-out files to CRLF. Prettier then compares against different bytes than the ones committed and reports 17 failures that do not exist. The files are fine in the repository; the working tree just has different line endings.

Fix: .gitattributes with * text=auto eol=lf. All four of us now see the same bytes regardless of platform, so pnpm format:check behaves identically everywhere. Shell scripts keep LF so shebangs are honoured, batch files get CRLF on purpose, and binary assets are excluded from normalization.

I also ran git add --renormalize . to check whether any committed file needed rewriting. The diff is empty, so this only fixes the checkout side.

Pushed to this branch. Could you re-run pnpm format:check on a fresh Windows checkout and confirm it is clean? If it still complains, the next suspect is Prettier's own endOfLine default and we can pin endOfLine: "lf" in .prettierrc.json as a belt-and-braces measure.

erikhfj added a commit that referenced this pull request Sep 28, 2026
Found by actually verifying rather than assuming: PR #34 targets
feat/1-repo-tooling because it is stacked on #32, and the workflow's
`pull_request: branches: [main]` filter silently skipped CI for it. The
push of 6ab73b6 produced no run at all.

Stacked PRs are how we are working (see docs/WORKFLOW.md), so a branch
filter on pull_request is wrong. pull_request now has no filter and runs
for every PR whatever it targets; push stays limited to main so we only
deploy from the mainline.

Co-Authored-By: opencode <noreply@opencode.ai>
Copy link
Member

@rachelks rachelks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-tested with a fresh Windows checkout and core.autocrlf=true. pnpm format:check now passes, so the .gitattributes fix resolves the issue I found.

@rachelks rachelks merged commit 16ef3cd into main Sep 28, 2026
erikhfj added a commit that referenced this pull request Sep 28, 2026
The workflow is on main for the first time. PR #34 merged into
feat/1-repo-tooling (its base at the time) after #32 had already merged
that branch into main, so the workflow never reached main and the repo
has been running with no CI at all. This brings it across.

Content is unchanged from what #34 reviewed and what has been passing on
the runner: quality job (format, lint, typecheck, test, build, bundle
budget) on every PR and every push to main, deploy job on push to main
only.

Two fixes that came out of actually running it:

- pull_request has no branch filter. A `branches: [main]` filter meant
  stacked PRs like #34, which targets feat/1-repo-tooling, silently got
  zero CI runs.

- deploy is pinned to [self-hosted, linux, x64, project2]. It writes to
  /var/www/html/project2 and restarts crate-api, which only exist on
  it2810-19. quality stays unpinned so any future runner can pick it up.

Co-Authored-By: opencode <noreply@opencode.ai>
Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scaffold pnpm monorepo (client / server / e2e)
2 participants